package utils

import (
	"fmt"
	"os"
	"os/signal"
	"server/utils/zaplog"
	"strconv"
	"strings"
	"syscall"
)

func InterfaceToString(value interface{}) (resStr string) {
	switch value.(type) {
	case int:
		op, _ := value.(int)
		resStr = strconv.Itoa(op)
	case uint8:
		op, _ := value.(uint8)
		resStr = strconv.FormatUint(uint64(op), 10)
	case uint16:
		op, _ := value.(uint16)
		resStr = strconv.FormatUint(uint64(op), 10)
	case uint32:
		op, _ := value.(uint32)
		resStr = strconv.FormatUint(uint64(op), 10)
	case uint64:
		op, _ := value.(uint64)
		resStr = strconv.FormatUint(uint64(op), 10)
	case int8:
		op, _ := value.(int8)
		resStr = strconv.FormatInt(int64(op), 10)
	case int16:
		op, _ := value.(int16)
		resStr = strconv.FormatInt(int64(op), 10)
	case int32:
		op, _ := value.(int32)
		resStr = strconv.FormatInt(int64(op), 10)
	case int64:
		op, _ := value.(int64)
		resStr = strconv.FormatInt(int64(op), 10)
	case string:
		resStr, _ = value.(string)
	default:
		zaplog.Error("InterfaceToString err type")
	}
	return
}

//转换为xorm框架执行的rawsql
func GetORMProcSql(procName string, inArgs, outArgs map[string]interface{}) string {
	var (
		//返回值
		retDeclare string = "declare @ReturnVal int "
		//output参数
		outDeclare  string
		rawSql      string = "%s EXEC @ReturnVal= [dbo].[%s] %s %s"
		args        string                                      //所有的输入和输出参数
		finalSelect string = "select @ReturnVal as 'ReturnVal'" //最后select需要返回的字段
	)
	//inArgs为所有输入参数,key:input参数名称,value:该参数的值
	for inArgName, inArgValue := range inArgs {
		args += fmt.Sprintf("%s=%s,", inArgName, InterfaceToString(inArgValue))
	}
	//去掉最后的","
	if len(outArgs) == 0 {
		args = strings.TrimRight(args, ",")
	}
	//outArgs为所有输出参数,key:input参数名称,value:该参数的类型
	for outArgName, outArgType := range outArgs {
		outDeclare += fmt.Sprintf("declare %s %s ", outArgName, InterfaceToString(outArgType))
		finalSelect += fmt.Sprintf(",%s as '%s',", outArgName, strings.TrimLeft(outArgName, "@"))
		args += fmt.Sprintf("%s=%s output,", outArgName, outArgName)
	}
	args = strings.TrimRight(args, ",")
	finalSelect = strings.TrimRight(finalSelect, ",")
	return fmt.Sprintf(rawSql, retDeclare+outDeclare, procName, args, finalSelect)
}

type checkFunc func() (res interface{})

func CheckRepeat(genName string, genFun checkFunc) (res chan string) {
	res = make(chan string)
	go func() {
		var (
			numMap = make(map[interface{}]bool, 1000000)
			time   uint64
		)
		defer func() {
			res <- fmt.Sprintf("%s repeated times:%d", genName, time)
		}()
		for {
			genNum := genFun()
			if _, ok := numMap[genNum]; ok {
				zaplog.Errorf("genNum:%s已存在", genNum)
				return
			} else {
				numMap[genNum] = true
			}
			time++
		}
	}()
	return
}

func GracefulExit() {
	c := make(chan os.Signal)
	// kill (no param) default send syscall.SIGTERM
	// kill -2 is syscall.SIGINT
	// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)

	for {
		signInfo := <-c
		zaplog.Infof("get a signal %s", signInfo.String())
		switch signInfo {
		case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
			zaplog.Info("shutting down ...")
			return
		case syscall.SIGHUP:
		default:
			return
		}
	}
}
